﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Threading;

namespace WPFMonad
{
   public interface View<R>
   {
       Answer Execute<Answer>(Func<R, Answer> f);
   }
    
   public class View1<TWpf,TContract> :View<TContract> where TWpf:UIElement,TContract
   {
       private readonly TWpf r;

        public View1(TWpf r)
        {
            this.r = r;
        }

        public Answer Execute<Answer>(Func<TContract, Answer> f)
       {
           Answer result = default(Answer);
           r.Dispatcher.Invoke(DispatcherPriority.Normal, (EventHandler)((sender, e)=> { result = f(r); }), null, null);
           return result;
       }
   }
   public class View2<R,TOld>:View<R>
   {
       public View2(View<TOld> old, Func<TOld, R> transformer)
       {
           this.old = old;
           this.transformer = transformer;
       }
       private readonly View<TOld> old;
       private readonly Func<TOld, R> transformer;
       public Answer Execute<Answer>(Func<R, Answer> f)
       {
          return old.Execute(o => f(transformer(o)));
       }
   }

    public enum Unit
    {
        Unit
    }
    public static class WPFMonadExtensions
    {
        public static View<U> SelectMany<T, U>(this View<T> m, Func<T, View<U>> k)
        {
            return m.Execute(k);
        }
        public static View<U> Select<T, U>(this View<T> m, Func<T, U> selector)
        {
            return new View2<U, T>(m, selector);
        }
        public static View<V> SelectMany<T, U, V>(this View<T> source, Func<T, View<U>> kSelector, Func<T, U, V> resultSelector)
        {
            return source.Execute(t => new View2<V, U>(kSelector(t), u => resultSelector(t, u)));
        }
        public static Unit Do(this View<Unit> k)
        {
            return k.Execute(id => id);
        }
        public static Answer Value<Answer>(this View<Answer> k) 
        {
            return k.Execute(id => id);
        }
        public static View<TView> AsView<TWPF, TView>(this TWPF value) where TWPF : UIElement, TView
        {
            return value.ToWpfMonad<TWPF, TView>();
        }

        public static View<TView> ToWpfMonad<TWPF, TView>(this TWPF value)
           where TWPF : UIElement, TView
        {
            return new View1<TWPF, TView>(value);
        }
    }

}
